#if GLF_AS_DLL
#pragma warning(disable:4275)
#endif

#include <glf/config.h>

// internal config header


#include <glf/config/platform.h>
#include <glf/config/compiler.h>
#include <glf/core/mutex.h>
#include <glf/core/infos.h>
#include <glf/debug/fileLogger.h>
#include <glf/io/fileStreamMgr.h>
#include <glf/io/fs.h>
#include <glf/input/inputManager.h>
#include <glf/debug/debugDisplay.h>
#include <glf/debugger/debugger.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>


#define GLFi_PLATFORM_HEADER(path)				GLF_TOSTRING(GLFi_PLATFORM_HEADER1(path, GLF_PLATFORM))
#define GLFi_PLATFORM_HEADER1(path, platform)	GLFi_PLATFORM_HEADER2(path, platform)
#define GLFi_PLATFORM_HEADER2(path, platform)	GLFi_PLATFORM_HEADER3(path, platform)
#define GLFi_PLATFORM_HEADER3(path, platform)	path##_##platform##_.hpp

#define GLFi_COMPILER_HEADER(path)				GLF_TOSTRING(GLFi_COMPILER_HEADER1(path, GLF_COMPILER))
#define GLFi_COMPILER_HEADER1(path, compiler)	GLFi_COMPILER_HEADER2(path, compiler)
#define GLFi_COMPILER_HEADER2(path, compiler)	GLFi_COMPILER_HEADER3(path, compiler)
#define GLFi_COMPILER_HEADER3(path, compiler)	path##_##compiler##_.hpp

#define GLFi_HEADER_API(path, api)				GLF_TOSTRING(GLFi_HEADER_API2(path,api))
#define GLFi_HEADER_API2(path, api)				GLFi_HEADER_API3(path,api)
#define GLFi_HEADER_API3(path, api)				path##_##api##_.hpp

#if GLF_USE_ASSERT

#define GLFi_ASSERTMSG(x, ...)	GLF_ASSERTMSG(x, __VA_ARGS__)

#else

#define GLFi_ASSERTMSG(x, ...)

#endif

#if GLF_USE_TRACE
	#define GLFi_PRINTLN Console::Println
#else
	#define GLFi_PRINTLN 
#endif

#define asizeof GLF_ASIZEOF

#if GLF_AS_DLL
extern T_FUNC_GLFALLOC GlfAllocFunc;
extern T_FUNC_GLFALLOC2 GlfAllocFunc2;
extern T_FUNC_GLFFREE GlfFreeFunc;
#endif

//application needs to implement those two functions
void* GlfAlloc(size_t size, glf::MemHint hint);
void  GlfFree(void* ptr);
void* GlfAlloc(size_t size);

#if GLF_NEW_NO_THROW
	#define GLF_THROW()
#else
	#define GLF_THROW() throw()
#endif


namespace glf {
enum NewType {
	NEWTYPE_CHECKSIZE
};
}

#if GLF_NO_ALLOC_IMPL

#define GLF_NEW_IMPL_NO_ARGS()	*new(mImplBuffer, sizeof(mImplBuffer)) Impl()
#define GLF_NEW_IMPL(...)		*new(mImplBuffer, sizeof(mImplBuffer)) Impl(__VA_ARGS__)
#define GLF_DELETE_IMPL()		m.~Impl()
#define GLF_CHECK_IMPL_SIZE()	GLF_STATIC_ASSERT_LE(sizeof(Impl), sizeof(mImplBuffer))

#else

#define GLF_NEW_IMPL_NO_ARGS()	*new (glf::MEMHINT_ZERO) Impl()
#define GLF_NEW_IMPL(...)		*new (glf::MEMHINT_ZERO) Impl(__VA_ARGS__)
#define GLF_DELETE_IMPL()		delete &m;
#define GLF_CHECK_IMPL_SIZE() 

#endif

#ifndef GLF_NO_NEW_DEFINE

#if GLF_AS_DLL
#else

void* operator new(size_t s, void* p) GLF_THROW();
//void* operator new[](size_t) GLF_THROW();
void  operator delete(void* p) GLF_THROW();
void  operator delete[](void*) GLF_THROW();
#endif

void* operator new(size_t, void*, size_t) GLF_THROW();

void* operator new[](size_t, void*) GLF_THROW();
void  operator delete(void*, void*) GLF_THROW();
void  operator delete(void*, void*, size_t) GLF_THROW();
void  operator delete[](void*, void*) GLF_THROW();

void  operator delete(void* p) GLF_THROW();
void  operator delete[](void* p) GLF_THROW();

#endif

inline void* operator new(size_t s, glf::MemHint hint) {
	return GlfAlloc(s, hint);
}
inline void* operator new[](size_t s, glf::MemHint hint) {
	return GlfAlloc(s, hint);
}
inline void  operator delete(void* p, glf::MemHint hint) {
	GlfFree(p);
}
inline void  operator delete[](void* p, glf::MemHint hint) {
	GlfFree(p);
}


inline void* operator new(size_t s, void* place, size_t maxSize) GLF_THROW()
{
	GLF_ASSERT(s <= maxSize);
	glf::MarkUsed(s);
	glf::MarkUsed(maxSize);

	return place;
}

inline void operator delete(void*, void*, size_t) GLF_THROW() {
	//do nothing
}

//TODO placement new: this probably wont compile on all platforms (already defined somewhere)
#ifndef __PLACEMENT_NEW_INLINE
#define __PLACEMENT_NEW_INLINE

#ifndef GLF_NO_NEW_DEFINE

#ifndef _NEW
#define _NEW
inline void* operator new(size_t, void* place) GLF_THROW() {

	return place;
}

inline void operator delete(void*, void*) GLF_THROW() {
	//do nothing
}
#endif

#endif

#endif

#include <glf/io/stream.h>

// TODO: for the moment, iThread.h is in the SOURCE folder of GLF ... Move this
//		file in the correct include folder and update !!ALL!! projects
#include "core/iThread.h"

namespace glf
{

struct ThreadMgr;
class Fs;
class App;

struct GlobalsBase 
{
	GlobalsBase()
	{
		argc = 0;

#if GLF_ENABLE_DEBUGGER
		eventManager.AddEventReceiver(glf::debugger::Debugger::GetInstance(), EventManager::EP_HIGH);
#endif
	}
	
	virtual ~GlobalsBase()
	{
#if GLF_ENABLE_DEBUGGER
		glf::debugger::Debugger* dbg = glf::debugger::Debugger::GetInstance();
		if(dbg) {
			eventManager.RemoveEventReceiver(dbg);
		}
#endif
	}

	ThreadMgr		threadMgr;
	PropertyMap		properties;
	DebugDisplay	debugDisplay;
	Fs				fileSystem;
	InputManager	inputMgr;
	EventManager	eventManager;

#if GLF_VIRTUAL_FILESTREAM
	FileStreamMgr   fileStreamMgr;
#endif

	FileLogger		fileLogger;

	App*			app;
	int				argc;
	char**			argv;
	SpinLock		mtxConsole;
};

}


#	include GLFi_HEADER_API(iConfig, GLF_API_CONFIG)

namespace glf {

struct Globals : public SpecificGlobals {
	Globals() {
	}

	virtual ~Globals() {
	}


};

Globals* GetGlobals();
void SetGlobals(Globals* pGlobals);

extern bool gEnableProfilerScopes;

void BeginProfilerEvent(const char* name);
void EndProfilerEvent();

struct MyProfilerScope {
	MyProfilerScope(const char* name) {
		if(gEnableProfilerScopes) {
			BeginProfilerEvent(name);
		}
	}
	~MyProfilerScope() {
		if(gEnableProfilerScopes) {
			EndProfilerEvent();
		}
	}
};

#define _ProfilerScopes(name, fn, file, line)	MyProfilerScope ps##line(name);
#define ProfilerScope(name)						_ProfilerScopes(name, __FUNCTION__, __FILE__, __LINE__)

}
